#include "Utils.h"
#include "Mod3Algorithm.h"
#include "Mod5Algorithm.h"
#include "GreedyAlgorithm.h"
#include <iostream>
#include <ctime>

#define ENABLE_LOG 0
#define INIT_STEP 0
#define ADD_3_STEP 1
#define ADD_2_STEP 2
#define ADD_4 0
#define ADD_2 1
#define FOUND_3_FIRST 0
#define FOUND_4_FIRST 1

UtilsPtr Utils::m_Instance;

Utils::Utils()
{
}

Utils::~Utils()
{
}

UtilsPtr Utils::GetInstance()
{
	if (!m_Instance)
	{
		m_Instance = UtilsPtr(new Utils());
	}
	return m_Instance;
}

//intialize
void Utils::Initialize(int al)
{
	time_t startt, endt;
	time(&startt);
#if ENABLE_LOG
	std::cout << "Init" << std::endl;
#endif
	if (al == MOD_3_AL)
	{
		m_algorithm = AlgorithmPtr(new Mod3Algorithm());
	}
	else if (al == MOD_5_AL)
	{
		m_algorithm = AlgorithmPtr(new Mod5Algorithm());
	}
	else if  (al == GREEDY_AL)
	{
		m_algorithm = AlgorithmPtr(new GreedyAlgorithm());
	}
	m_battle = BattleShipPtr(new BattleShip());
	m_algorithm->Initialize(m_battle);
	m_state = BEGIN;
	m_swap = (rand() % 2) == 0;
#if ENABLE_LOG
	std::cout << "m_swap: " <<m_swap<<std::endl;
#endif
#if ENABLE_LOG
	std::cout << "Size:" << m_checkPoint.size() << std::endl;
#endif

	time(&endt);
#if ENABLE_LOG	
	std::cout << "Init done" << difftime(endt,startt) << std::endl;
#endif
	//m_thread = new ThreadType(Utils::ThreadRunning);
}//end


//guess a position
Point Utils::GuessPos()
{
	Point result = Point(0,0);
	float max_result = 0;
	switch (m_state) {
		case BEGIN:
		case FINDING:
			{
				//clean up before guess pos
				result = m_algorithm->GuessPos();

				if (!m_battle->IsUnkown(result))
				{
					std::cout << "ERROR, We known this point"<<std::endl;
				}
				break;
			}
		case FOUND_ONE:
			{
				//clean up before guess pos
				//CleanUp(); //this also need clean up
				result = m_algorithm->GuessDirection(m_lastPoint,m_direction);
                if (!m_battle->IsUnkown(result))
				{
					std::cout << "ERROR, We known this point"<<std::endl;
				}

				break;
			}
		case FOUND_TWO:
			{
				//clean up before guess pos
				//CleanUp(); //not sure, this need clean up

				int k = 0;
				if (m_direction == Ship::VERTICAL)
				{
					k = 1;
				}
				Point newP1 = m_lastPoint;
				Point newP2 = m_lastPoint;
				while (true)
				{
					if (m_battle->IsInShip(newP1))
					{
						newP1.first = newP1.first + s_dx[k];
						newP1.second = newP1.second + s_dy[k];
						if (m_battle->IsUnkown(newP1))
						{
							result = newP1;
							break;
						}
					}

					if (m_battle->IsInShip(newP2))
					{
						newP2.first = newP2.first - s_dx[k];
						newP2.second = newP2.second - s_dy[k];
						if (m_battle->IsUnkown(newP2))
						{
							result = newP2;
							break;
						}
					}
				}

                if (!m_battle->IsUnkown(result))
				{
					std::cout << "ERROR, We known this point"<<std::endl;
				}

				break;
			}
		default:
			break;
	}
#if ENABLE_LOG	
	std::cout << "Guess " << result.first << ":"<<result.second << std::endl;
#endif
	return result;
}//end

void Utils::ProcessPos(Point p, bool value)
{
#if ENABLE_LOG	
	std::cout << "Found " << p.first << ":" << p.second << ":"<< value <<std::endl;
#endif
	m_algorithm->FoundPos(p,value);
	if (value)
	{
		switch (m_state) {
		case BEGIN:
		case FINDING:
			m_lastPoint = p;
			m_state = FOUND_ONE;
			break;
		case FOUND_ONE:
			m_state = FOUND_TWO;
			break;
		case FOUND_TWO:
			break;
		default:
			std::cout << "ERROR" << std::endl;
			break;
		}
	}
	else
	{
		switch (m_state) {
		case BEGIN:
		case FINDING:
		case FOUND_ONE:
		case FOUND_TWO:
			break;
		default:
			std::cout << "ERROR" << std::endl;
			break;
		}
	}
}

//prepare for new set
void Utils::EndSet()
{
}//end

//found a ship, process it
void Utils::FoundShip(Point p, Ship::TypeOfShip _type)
{
#if ENABLE_LOG	
	std::cout << "Found ship: %d" << _type << std::endl;
#endif
	m_state = FINDING;
	ShipPtr ship = m_battle->FindShip(p,_type);
	if (ship)
	{
		m_algorithm->FoundShip(ship);
		//remove outside points
		PointSet pointSet = ship->GetSpaceForShip();
		PointSet::iterator ii;
		for (ii = pointSet.begin(); ii != pointSet.end(); ii++)
		{
			if (m_battle->IsUnkown(*ii))
			{
				m_algorithm->FoundPos(*ii,false);
			}
		}
	} 
	else
	{
		std::cout << "CLGT, we can't find ship" << std::endl;
	}
}
//end

void Utils::SetBoard(BoardType &result)
{
	BattleShipPtr battle (new BattleShip());

	SetAirCraft(battle,result[Ship::AIRCRAFT]); // put aircraft,

	//put other ships
	for (int i=Ship::BATTLE; i>= Ship::DESTROYER;i--)
	{
		result[i][0] = Ship::GetLength((Ship::TypeOfShip)i);
		ShipPtr ship;
		int x,y,r;
		do 
		{
			x = rand() % 10;
			y = rand() % 10;
			r = (rand() % 2);
			ship = ShipPtr(new Ship((Ship::TypeOfShip)i,x,y,(Ship::Rotation)r));
		} while (!battle->PutShip(ship));
		result[i][1] = x;
		result[i][2] = y;
		result[i][3] = 1 - r;
		#if ENABLE_LOG	
		std::cout << "x: " <<x<<", y: "<<y<<", r:"<<r<<std::endl;
		#endif
	}
	#if ENABLE_LOG	
	for (int i=0;i<5;i++)
	{
		std::cout << result[i][0] <<":"<<result[i][1]<<":"<<result[i][2]<<":"<<result[i][3]<<std::endl;
	}
	#endif
}

//put aircraft on battle
void Utils::SetAirCraft(BattleShipPtr battle, int shipArray [4])
{
	int type = rand() % 4;
	int x,y,r;
	ShipPtr ship;
	do {
		switch (type)
		{
		case 0:
			x = 0;
			y = rand() % 10;
			r = Ship::HORIZONTAL;
			break;
		case 1:
			x = 9;
			y = rand() % 10;
			r = Ship::HORIZONTAL;
			break;
		case 2:
			x = rand() % 10;
			y = 0;
			r = Ship::VERTICAL;
			break;
		case 3:
			x = rand() % 10;
			y = 0;
			r = Ship::VERTICAL;
			break;
		}
		ship = ShipPtr(new Ship(Ship::AIRCRAFT,x,y,(Ship::Rotation)r));
	} while (!battle->PutShip(ship));
	shipArray[0] = Ship::GetLength(Ship::AIRCRAFT);
	shipArray[1] = x;
	shipArray[2] = y;
	shipArray[3] = 1 - r;
}
//end

void Utils::Swap(int &x, int &y)
{
#if ENABLE_LOG
	return;
#endif
	if (!m_swap) return;
	x = 9 - x;
}